unit mEDAProject ;

interface

uses
    SysUtils,
    Classes,
    Graphics,
    Windows,
    MedaxData,
    ExtCtrls,
    ComCtrls,
    Controls,
    Forms,
    MedaxUtils,
    Math,
    Gr32v1_5,
    dtpShape,
    dtpLineShape,
    dtpGroupShape,
    dtpPolygonShape,
    dtpTextShape,
    dtpDocument ;

type
    TmEDALayerObjectType = ( lotEllipse, lotRectangle, lotRoundRect,
        lotPolygon, lotHTrace, lotVTrace ) ;
    TmEDAPageObjectType = ( potBox, potPin, potHLine, potVLine, potText ) ;

    TmEDAElementType = ( etNone, etModule, etBox, etPin, etShape, etPad,
        etNet, etSignal ) ;

    TmEDALayerType = ( lcPaste, lcLayout, lcComponent, lcSolder, lcGround,
        lcPower, lcInner0, lcInner1, lcInner2, lcInner3, lcMechanical ) ;
    TmEDALayerTypeSet = set of TmEDALayerType ;

    TmEDAPadType = ( ptEllipse, ptRectangle, ptRoundRect, ptOctagon, ptPolygon, ptTermal ) ;

    TmEDAPageRange = 0..255 ;
    TmEDAPageRangeSet = set of TmEDAPageRange ;

    TmEDADir = ( drNorth, drEast, drSouth, drWest ) ;
    TmEDADirSet = set of TmEDADir ;

    TmEDALabelType = ( ltPlain, ltOutput, ltInput, ltOutPage, ltInPage,
        ltPower, ltGround ) ;

    TmEDAHoleType = ( htEllipse, htRoundRect, htOctagon, htPolygon ) ;

    TmEDAPoint = record
        X, Y : integer ;
    end ;

    TPolygon = array of TRect ;

const
    drHorizontal    : TmEDADirSet = [ drEast, drWest ] ;
    drVertical      : TmEDADirSet = [ drNorth, drSouth ] ;

    {: On the PCB we use mics as a unit. One mil is 25.4 micron. A mic 0.1 micron.
    The half a mil is then 127 mics.

    On the schamatic we use mm's in extended format.
    }

const
    MicsPerMM       = 10000 ;
    MicsPerInch     = 254000 ;
    MicsPerMil      = 254 ;

type

    TmEDAObject = class
    private
        { Private declarations }
    public
        { Public declarations }
    end ;

    TmEDALayerObject = class
    private
        { Private declarations }
        fShape : TdtpPolygonShape ;
    public
        { Public declarations }
        procedure PublishOnPCB( Doc : TdtpDocument ) ; virtual ;
    end ;

    TmEDAPageObject = class
    private
        { Private declarations }
        FSchemaGroup : TdtpGroupShape ;
    protected
        { Protected declarations }
    public
        { Public declarations }
        procedure PublishOnSchema( Doc : TdtpDocument ) ; virtual ; abstract ;
    end ;

    TDMmEDAProject = class ;
    TmEDAModule = class ;
    TmEDAShape = class ;

    TmEDAElement = class ;
    TmEDASymbol = class ;
    TmEDANet = class ;
    TmEDAPad = class ;
    TmEDAPin = class ;

    TmEDALine = class ;
    TmEDATrack = class ;

    TmEDAElement = class( TmEDAObject )
    private
        { Private declarations }
        fName : string ;
        fType : TmEDAElementType ;
    public
        { Public declarations }
        constructor Create( aName : string ; aType : TmEDAElementType ) ;
        destructor Destroy ; override ;
        property Name : string read FName ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; virtual ; abstract ;
        procedure PublishOnSchema( Doc : TdtpDocument ) ; virtual ; abstract ;
    end ;

    {: A component is a representation of a physical electronic component. It has
    at least one schematic appearance and a shape symbol on the PCB, both need
    not be plaed yet. A component has one or more pins; each pin can be
    connected to one net.
    }
    TmEDAComponent = class( TmEDAElement )
    private
        { Private declarations }
        FModule : TmEDAModule ;                   { owning module }
        FSymbols : TList ;                        { all Symbols of this Component }
        FShape : TmEDAShape ;                     { the Shape of this Component }
    public
        { Public declarations }
        constructor Create( aName : string ; aModule : TmEDAModule ) ;
        destructor Destroy ; override ;
        function AddBox( Name, Family, Part : string ; Page : TmEDAPageRange ; X, Y : integer ) : TmEDASymbol ;
        property Shape : TmEDAShape read FShape ;
        property Module : TmEDAModule read FModule ;
        procedure PublishOnSchema( Doc : TdtpDocument ) ; override ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; override ;
    end ;

    {: A net is a collection of connections between pins. A net joins all the pins
    that are connected. The connections can be represented by a line in a schematic,
    an occurrence of the name of the net a s a label or a connection through a bus.
    The physical representations on a PCB are traces and vias.
    }
    TmEDANet = class( TmEDAElement )
    private
        { Private declarations }
        FModule : TmEDAModule ;                   { owning module }
        FPins : TList ;                           { all pins connected to this net }
        FLines : TList ;                          { all lines of this net }
        FTraces : TList ;                         { PCB traces of the net }
        FVias : TList ;                           { PCB vias of the net }
    public
        { Public declarations }
        constructor Create( aModule : TmEDAModule ; aName : string ) ;
        procedure AddLine( aLine : TmEDALine ) ;
        procedure AddTrace( aTrace : TmEDATrack ) ;
        procedure Join( Pin : TmEDAPin ) ; overload ;
        procedure Join( Net : TmEDANet ) ; overload ;
        procedure PublishOnSchema( Doc : TdtpDocument ) ; override ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; override ;
    end ;

    {: A bus is a component like object with pins. Pins can be added to a bus and
    connected to one or more of the other pins in the bus. A bus can have a
    label. Two busses with the same label bundle the same set of pins. The pins
    with the same name or number have are interconnected. Other pins are not.
    Pins with a number as name have the name bus as genaral name indexed by that
    very number. It can be useful in (bus)routing on a PCB.
    }
    TmEDABus = class( TMEDAElement )
    private
        fModule : tmEDAModule ;                   { containig module }
        fBusPins : array of TmEDABusPin ;         { array length is bus width }
        fLines : array of tmEDALines ;            { number of lines }
    public
        constructor Create( aModule : TmEDAModule ; aName : string ) ;
        function AddPin( aIndex : integer ; aNet : TmEDANet ) : TmEDABusPin ;
        procedure PublishOnSchema( Doc : TdtpDocument ) ; override ;
    end ;

    {: A bus pin is a collection of pins, regarded interconnected, within a bus.
    A bus pin has either a number as name or a name itself. In the latter case
    the containing bus is a bundle of unrelated nets. In the formaer case the bus
    is indexed by the number name as index.
    }
    TmEDABusPin = class( TMEDAElement )
    private
        fBus : tmEDABus ;                         { containig bus }
        FPins : array of TmEDAPin ;               { array length number of pins}
    public
        constructor Create( aBus : TmEDABus ; aName : string ) ;
        procedure AddPin( aPin : tmEDAPin ) ;
        { Draws a funny connection to the main bus lines }
        procedure PublishOnSchema( Doc : TdtpDocument ) ; override ;
    end ;

    {: A line is a schematic representation of a (part) of a net. A net can have
    several nets. A line ends in either a pin of a component or a bus, or
    a label or an other line of the same net.
    }
    TmEDALine = class( TMEDAPageObject )
    private
        FNet : tmEDANet ;                         { line is part of this net }
        fP1, fP2 : TmEDAPoint ;
    public
        constructor Create( aNet : TmEDANet ) ;
        procedure PublishOnSchema( Doc : TdtpDocument ) ; override ;
    end ;

    {: A shape is the footprint of a component on either the component or solder
    layer of a PCB. It consists of pads, padstacks and hole. A pin can be
    connected to several pads or padstacks. A hole has no electrical connection.
    }
    TmEDAShape = class( TmEDAElement )
    private
        { Private declarations }
        FModule : TmEDAModule ;                   { Hyperlink: Module of Box appearance }
        FWidth, FHeight : integer ;               { Shape width and height in mics }
        FXpos, FYPos : integer ;                  { Shape position on layer in mics }
        FRotation : TmEDADir ;                    { angular rotation of Shape in microradians, only right angled for the moment }
        FLayer : TmEDALayerType ;                 { Layer of appearance }
        FPads : TList ;                           { Pads of the Shape }
    public
        { Public declarations }
        constructor Create( aComp : TmEDAComponent ; aShape : string ; X, Y : integer ; aLayer : TmEDALayerType ; aOrient : TmEDADir ) ;
        destructor Destroy ; override ;
        property X : integer read FXpos write FYPos ;
        property Y : integer read FYpos write FYPos ;
        property Width : integer read FWidth ;
        property Height : integer read FHeight ;
        property Layer : TmEDALayerType read FLayer write FLayer ;
    end ;

    {: A symbol is part of the set of symbols that represent a component. A
    component be reprented by a set of equal symbols, or a set of different
    symbols. Each of the symbols are initially not placed (part of a schematic).
    If it is placed it can be published (part of a document).
    }
    TmEDASymbol = class( TmEDAElement )
    private
        { Private declarations }
        FModule : TmEDAModule ;                   { Hyperlink: Module of Box appearance }
        fPage : TmEDAPageRange ;                  { assigned if placed on a page }
        FXpos, FYPos : extended ;                 { Box position on sheet in mm }
        FFamily, FPart : string ;
        FWidth, FHeight : extended ;              { Box width and height in mm }
        fComponent : TmEDAComponent ;             { Part of this component }
    public
        { Public declarations }
        constructor Create( aComp : TmEDAComponent ; Name, Family, Part : string ) ;
        property Component : TmEDAComponent read FComponent ;
        procedure Place( aPage : TmEDAPageRange ; X, Y : extended ) ; { put the Symbol pn one of the pages }
        procedure PublishOnSchema( Doc : TdtpDocument ) ; override ;
    end ;

    {: A pin is a part of a component. The pins of various components can be
    interconnected by nets. Only one net can connect to a pin.
    }
    TmEDAPin = class( TmEDAElement )
    private
        { Private declarations }
        fComponent : TmEDAComponent ;             { Containing component }
        fType : TmEDADir ;                        { Pin position on symbol edge }
        fNumber : integer ;                       { Pin number value }
        fX, fY : extended ;                       { Part of this component }
    public
        { Public declarations }
        constructor Create( aComp : TmEDAComponent ; aName : string ; aNumber : integer ; aOrient : TmEDADir ) ;
        destructor Destroy ; override ;
        procedure PublishOnSchema( Doc : TdtpDocument ) ; override ;
    end ;

    {: A hole is a round (drilled) of arbitrary (milled) shaped hole in the PCB.
    A milled hole uses a fDiameter milling bit.  If a hole is insize a net, chance
    is that this hole will be usable as a electrical interconnect. The pads
    associated with the hole should all be connected to one net.
    }
    TmEDAHole = class( TmEDAElement )
    private
        { Private declarations }
        fXpos, FYPos : integer ;                  { Hole position in mics }
        fType : TmEDAHoleType ;                   { Hole type }
        fDiameter : integer ;                     { Hole size in mics }
        fOutline : TPolygon ;                     { in case of a polygon }
        function GetPoints( i : integer ) : TmEDAPoint ;
    public
        { Public declarations }
        constructor Create( aComp : TmEDAComponent ; aName : string ; aNumber : integer ; aOrient : TmEDADir ) ;
        destructor Destroy ; override ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; override ;
    end ;

    {: A padstack is a mechanical hole in the PCB with electrical connections to
    one or more layers. Each layer has a different pad or no pad. All pads of a
    padstack are electrically connected. The hole is a separate object.
    }
    TmEDAStack = class( TmEDAElement )
    private
        { Private declarations }
        fXpos, FYPos : integer ;                  { Pad position in mics }
        fHole : TmEDAHole ;                       { optional hole of the stack }
        fPads : TList ;                           { set of Pads of the stack }
    public
        { Public declarations }
        constructor Create( aPin : TmEDAPin ; aOrient : TmEDADir ; X, Y : integer ) ;
        destructor Destroy ; override ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; override ;
    end ;

    {: A pad is a patch of metal on one layer of the PCB. A pad can have positive
    or negative appearance. A pad is represented by one or more graphic elements.
    A pad can be intersected by several traces of the same net.
    }
    TmEDAPad = class( TmEDAElement )
    private
        { Private declarations }
        FStack : TmEDAStack ;                     { Pad is part of this stack }
        FPadWidth : integer ;                     { width of pad on the layer }
        FPadHeight : integer ;                    { height of pad on the layer }
        FPadType : TmEDAPadType ;                 { Pad type, Round, Rectangle, Rounded Rectangle, ... }
        FLayerType : TmEDALayerType ;             { the pad is on this layer }
        function GetPadWidth : integer ;
        procedure SetPadWidth( NewValue : integer ) ;
        function GetPadHeight : integer ;
        procedure SetPadHeight( NewValue : integer ) ;
    public
        { Public declarations }
        constructor Create(
            Owner : TmEDAStack ;
            aLayer : TmEDALayerType ;
            aType : TmEDAPadType ;
            aOrient : TmEDADir ;
            X, Y : integer ) ;
        destructor Destroy ; override ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; override ;
    end ;

    {: A via is a instatiation of a padstack for connection purposes. It allows
    connection between layers. It belongs to a net.
    }
    TmEDAVia = class( TmEDALayerObject )
    private
        { Private declarations }
        FNet : TmEDANet ;
        FXpos, FYPos : integer ;                  { Via position in mics }
        FStack : TmEDAStack ;                     { Via Stack definition }
    public
        { Public declarations }
        constructor Create( X, Y : integer ) ;
        destructor Destroy ; override ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; override ;
    end ;

    {: A label is a schematic object of a net. A label can be attched to a line
    or a pin. All equally named labels are part of the same net. A label can have
    various graphical representations, indicating ports, pagerefs etc.
    }
    TmEDALabel = class( TmEDAElement )
    private
        { Private declarations }
        FNet : TmEDANet ;
        FType : TmEDALabelType ;
    public
        { Public declarations }
        constructor Create( aName : string ) ;
        destructor Destroy ; override ;
    end ;

    {: A track is a collection of line and polygon segments join together to one
    metal track. A track connects various pads by intersectiong with them. A pad
    can be fully inside a track.
    }
    TmEDATrack = class( TmEDAElement )
    private
        { Private declarations }
        FNet : TmEDANet ;
    public
        { Public declarations }
        constructor Create( aName : string ) ;
        destructor Destroy ; override ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; override ;
    end ;

    {: A layer can be a copper layer, a mechanical annotation layer etc. A
    component which is placed on a PCB puts information in some layers. The pads
    of its shape are put in one copper layer and its associated layout, paste and
    screen layers. The padstacks do so in all copper layers. Additional holes can
    be added to the tooling layer. During routing, traces are placed on copper
    layers and vias are used to cross layers.
    }
    TmEDALayer = class( TmEDAElement )
    private
        { Private declarations }
        fObjects : TmEDALayerObject ;
    public
        { Public declarations }
        constructor Create( layer : TmEDALayerType ) ;
        destructor Destroy ; override ;
        procedure Add( obj : TmEDALayerObject ) ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; override ;
    end ;

    {: A PCB is a set of layers. There can be only one or none of each layer. A
    PCB can be published as a document or exported as a (set of) Gerber file(s)
    and mechanical tooling (drilling and milling) files.
    }
    TmEDAPCB = class( TmEDAObject )
    private
        { Private declarations }
        FMedaLayers : array[ TmEDALayerType ] of TmEDALayer ;
        FCurrentLayer : TmEDALayerType ;          { the current active layer }
        fPCBWidth : extended ;
        fPCBHeight : extended ;
        fOrigin : TFloatPoint ;
    public
        { Public declarations }
        procedure AddLayerObject( Owner : TmEDAElement ; layer : TmEDALayerType ; otType : TmEDALayerObjectType ; X, Y, Width, Height : integer ) ;
        procedure CreateLayers( layers : TmEDALayerTypeSet ) ;
        property CurrentLayer : TmEDALayerType read FCurrentLayer write FCurrentLayer ;
        procedure PublishOnPCB( Doc : TdtpDocument ) ; virtual ;
    end ;

    {: A schematic is a set of pages. Eah page contains placed schematic symbols,
    lines and labels. Further it can contain annotations, textboxes, pageframe,
    legend, title, dates etc.
    }
    TmEDASchema = class( TmEDAObject )
    private
        { Private declarations }
        FMedaPages : array[ TmEDAPageRange ] of TmEDAModule ;
        FCurrentPage : TmEDAPageRange ;           { the current active page }
        FSheetWidth, FSheetHeight : extended ;    { sheet width and Height in Meda units }
    public
        { Public declarations }
        procedure CreatePages( pages : TmEDAPageRangeSet ) ;
        property CurrentPage : TmEDAPageRange read FCurrentPage write FCurrentPage ;
    end ;

    {: A module is a collection of components, which can be instatiated as a
    component itself. It can be regarded as a separate PCB, and as completed layout
    placed on the main PCB. It has its own schematic and shape.
    }
    TmEDAModule = class
    private
        { Private declarations }
    public
        { Public declarations }
    end ;

    {: The project is a container for all components, nets, tooling etc.
    }
    TDMmEDAProject = class( TDataModule )
    private
        { Private declarations }
        FName : string ;
        FModules : TList ;
        FCurrentModule : TmEDAModule ;            { the current active module }
        FPCB : TmEDAPCB ;
        function GetModule( i : integer ) : TmEDAModule ;
    public
        { Public declarations }
        function AddModule( Name : string ) : TmEDAModule ;
        property CurrentModule : TmEDAModule read FCurrentModule write FCurrentModule ;
        property Modules[ i : integer ] : TmEDAModule read GetModule ;
        property PCB : TmEDAPCB read FPCB ;
    published
        { Published declarations }
        property Name : string read FName ;
    end ;

var
    Project         : TDMmEDAProject ;

implementation

{$R *.DFM}

function mEDAPoint( aX, aY : integer ) : TmEDAPoint ;
begin
    Result.X := aX ;
    Result.Y := aY ;
end ;

{ TmEDALayerObject }

procedure TmEDALayerObject.PublishOnPCB( Doc : TdtpDocument ) ;
var
    w               : extended ;
begin
    with Doc, FRect do begin
        case FType of
            otEllipse : Shape := TdtpEllipseShape.Create ;
            otRectangle : Shape := TdtpRectangleShape.Create ;
            otHTrace, otVTrace, otRoundRect : begin
                    if Right - Left < Bottom - Top then
                        w := Right - Left
                    else
                        w := Bottom - Top ;
                    Shape := TdtpRoundRectShape.Create ;
                end ;
        else
            Exit ;
        end ;
        Shape.Tag := integer( Self ) ;            // backlink
        Doc.ShapeAdd( Shape ) ;
        Shape.DocRect := FRect ;
    end ;
end ;

{ TmEDAPageObject }

constructor TmEDAPageObject.Create( Owner : TmEDAElement ; otType : TmEDAPageObjectType ; X, Y, Width, Height : integer ) ;
begin
end ;

procedure TmEDAPageObject.Publish( Doc : tdtpDocument ) ;
var
    Shape           : TdtpShape ;
    R               : TFloatRect ;
begin
    case FType of
        otBox : begin
                Shape := TdtpRectangleShape.Create ;
                with Shape as TdtpRectangleShape do begin
                    Tag := integer( Self ) ;
                    Doc.ShapeAdd( Shape ) ;
                    with FRect do
                        DocRect := FloatRect( Left, Top, Right, Top ) ;
                end ;
                Shape := TdtpTextShape.Create ;
                with Shape as TdtpTextShape do begin
                    Text := FOwner.Name ;
                    Tag := integer( Self ) ;
                    Doc.ShapeAdd( Shape ) ;
                    with FRect do
                        DocRect := FloatRect( Left, Top, Right, Top ) ;
                end ;
            end ;
    else
        Shape := TdtpLineShape.Create ;
        with Shape as TdtpLineShape do begin
            Tag := integer( Self ) ;
            Doc.ShapeAdd( Shape ) ;
            with FRect do
                DocRect := FloatRect( Left, Top, Right, Bottom ) ;
            Shape := TdtpTextShape.Create ;
            with Shape as TdtpTextShape do begin
                Text := FOwner.Name ;
                Tag := integer( Self ) ;
                Doc.ShapeAdd( Shape ) ;
                with FRect do
                    DocRect := FloatRect( Left, Top, Right, Top ) ;
                if Self is TmEDAPin then
                    with Self as TmEDAPin do
                        case Direction of
                            drNorth : DocRotate( 90 ) ;
                            drSouth : DocRotate( 90 ) ;
                            drWest : ;
                        end ;
            end ;
        end ;
    end ;
end ;

{ TmEDAProject }

constructor TmEDAProject.Create( Name : string ) ;
var
    i               : TmEDALayerType ;
begin
    inherited Create ;
    FName := Name ;
    FModules := TList.Create ;
    FCurrentLayer := lcComponent ;
    for i := Low( TmEDALayerType ) to High( TmEDALayerType ) do
        FMedaLayers[ i ] := TmEDALayer.Create( i ) ;
end ;

function TmEDAProject.AddModule( Name : string ) : TmEDAModule ;
begin
    Result := TmEDAModule.Create( Name, Self ) ;
    FModules.Add( Result ) ;
    FCurrentModule := Result ;
end ;

procedure TmEDAProject.AddLayerObject( Owner : TmEDAElement ; layer : TmEDALayerType ; otType : TmEDALayerObjectType ; X, Y, Width, Height : integer ) ;
begin
    FMedaLayers[ layer ].Add( TmEDALayerObject.Create( Owner, otType, X, Y, Width, Height ) ) ;
end ;

procedure TmEDAProject.PaintLayer( Document : TdtpDocument ; l : TmEDALayerType ) ;
var
    i               : integer ;
begin
    if FMedaLayers[ l ] <> nil then
        with FMedaLayers[ l ] do
            for i := 0 to Count - 1 do
                Items[ i ].Publish( PaintBox ) ;
end ;

function TmEDAProject.LayerHitTest( l : TmEDALayerType ; X, Y : integer ) : boolean ;
var
    i               : integer ;
begin
    if FMedaLayers[ l ] <> nil then
        with FMedaLayers[ l ] do
            for i := 0 to Count - 1 do
                if Items[ i ].HitTest( X, Y ) then begin
                    Result := True ;
                    Exit ;
                end ;
    Result := False ;
end ;

destructor TmEDAProject.Destroy ;
begin
    inherited Destroy ;
end ;

procedure TmEDAProject.CreateLayers( layers : TmEDALayerTypeSet ) ;
begin

end ;

procedure TmEDAProject.CreatePages( pages : TmEDAPageNumberSet ) ;
begin

end ;

{ TmEDAElement }

constructor TmEDAElement.Create( Name : string ; EType : TmEDAElementType ) ;
begin
    inherited Create ;
    FName := Name ;
    FType := EType ;
end ;

function TmEDAElement.AsPin : TmEDAPin ;
begin
    Result := TmEDAPin( Self ) ;
end ;

function TmEDAElement.AsNet : TmEDANet ;
begin
    Result := TmEDANet( Self ) ;
end ;

function TmEDAElement.AsBox : TmEDABox ;
begin
    Result := TmEDABox( Self ) ;
end ;

destructor TmEDAElement.Destroy ;
begin
    inherited Destroy ;
end ;

{ TmEDAModule }

constructor TmEDAModule.Create( Name : string ; Project : TmEDAProject ) ;
begin
    inherited Create( Name, etModule ) ;
    FProject := Project ;
    FBoxes := TList.Create ;
    FNets := TList.Create ;
    FMedaPageData := TList.Create ;
    FGrid := True ;
end ;

function TmEDAModule.AddBox( Name, Family, Part : string ; X, Y : integer ) : TmEDABox ;
begin
    Result := TmEDABox.Create( Self, Name, Family, Part, X, Y ) ;
    FBoxes.Add( Result ) ;
end ;

function TmEDAModule.AddNet( Name : string ) : TmEDANet ;
begin
    Result := TmEDANet.Create( Self, Name ) ;
    FNets.Add( Result ) ;
end ;

procedure TmEDAModule.AddPageObject( Owner : TmEDAElement ; otType : TmEDAPageObjectType ; X, Y, Width, Height : integer ) ;
begin
    FMedaPageData.Add( TmEDAPageObject.Create( Owner, otType, X, Y, Width, Height ) ) ;
end ;

procedure TmEDAModule.Publish( Document : TdtpDocument ) ;
var
    i, j, hFont     : integer ;
begin
    with PaintBoxH do begin
        Canvas.Font.Name := 'Courier New' ;
        Canvas.Font.Height := Tag ;
        Canvas.Font.Color := clNavy ;
        Canvas.Font.Style := [ fsBold ] ;
        SetBkMode( Canvas.Handle, TRANSPARENT ) ;
        if Self.FGrid and ( Tag > 8 ) then
            for i := -Left div Tag to -Left div Tag + 80 do
                for j := -Top div Tag to -Top div Tag + 60 do
                    SetPixel( Canvas.Handle, i * Tag, j * Tag, $007F00 ) ;
        Canvas.MoveTo( 0, 0 ) ;
        Canvas.LineTo( Width - 1, 0 ) ;
        Canvas.LineTo( Width - 1, Height - 1 ) ;
        Canvas.LineTo( 0, Height - 1 ) ;
        Canvas.LineTo( 0, 0 ) ;
    end ;
    with PaintBoxV.Canvas do begin
        Font.Color := clNavy ;
        { no font orientation in Delphi }
        hFont := CreateFont( PaintBoxH.Tag, 0, 900, 900, FW_BOLD, 0, 0, 0, 0, 0, 0, 0, 0, 'Courier New' ) ;
        hFont := SelectObject( Handle, hFont ) ;
        SetBkMode( Handle, TRANSPARENT ) ;
    end ;
    if FMedaPageData <> nil then
        with FMedaPageData do
            for i := 0 to Count - 1 do
                TmEDAPageObject( Items[ i ] ).Publish( PaintBoxH, PaintBoxV ) ;
    SelectObject( PaintBoxV.Canvas.Handle, hFont ) ;
    DeleteObject( hFont ) ;
end ;

function TmEDAModule.HitTest( X, Y, Zoom : Integer ) : TmEDAElement ;
var
    i               : integer ;
begin
    Result := nil ;
    X := ( X + Zoom div 2 ) div Zoom ;
    Y := ( Y + Zoom div 2 ) div Zoom ;
    if FMedaPageData <> nil then
        with FMedaPageData do
            for i := Count - 1 downto 0 do
                if TmEDAPageObject( Items[ i ] ).HitTest( X, Y ) then begin
                    Result := TmEDAPageObject( Items[ i ] ).Owner ;
                    Break ;
                end ;
end ;

procedure TmEDAModule.PlaceOnPCB( NameOfBox, PCBShape : string ; X, Y : integer ; Layer : TmEDALayerType ; Rotation : TmEDADir ) ;
var
    i               : integer ;
    Box             : TmEDABox ;
begin
    for i := 0 to FBoxes.Count - 1 do begin
        Box := TmEDABox( FBoxes.Items[ i ] ) ;
        if Box <> nil then
            if Box.Name = NameOfBox then begin
                Box.PlaceShape( PCBShape, X, Y, Layer, Rotation ) ;
                Break ;
            end ;
    end ;
end ;

destructor TmEDAModule.Destroy ;
begin
    inherited Destroy ;
end ;

{ TmEDANet }

constructor TmEDANet.Create( Module : TmEDAModule ; Name : string ) ;
begin
    inherited Create( Name, etNet ) ;
    FModule := Module ;
    FPins := TList.Create ;
    FLines := TList.Create ;
    FLabels := TList.Create ;
    FTraces := TList.Create ;
    FVias := TList.Create ;
end ;

procedure TmEDANet.Join( Net : TmEDANet ) ;
begin
    // join other net to us by:
      // check if same module
    if FModule <> Net.Module then
        Exit ;
    // copy all lines, pins, traces, vias and labels
    // assign all pageobjects to us
    // free other net
end ;

procedure TmEDANet.AddLine( otType : TmEDAPageObjectType ; X, Y, Z : integer ) ;
begin
    FModule.AddPageObject( Self, otType, X, Y, Z, 1 ) ;
    //	FLines.Add(  ) ;
end ;

procedure TmEDANet.AddPin( Pin : TmEDAPin ) ;
begin
    FPins.Add( Pin ) ;
end ;

{ TmEDAShape }

constructor TmEDAShape.Create( Box : TmEDABox ; Shape : string ; X, Y : integer ; Layer : TmEDALayerType ; Rotation : TmEDADir ) ;
var
    XPad, YPad, i   : integer ;
    MyStack0, MyStack1 : TmEDAStack ;
    nPinSpacing, nRowSpacing, nColumnSpacing : integer ;
    sType, sPad     : string ;
    Pin             : TmEDAPin ;
begin
    inherited Create( Box.Name, etShape ) ;
    FBox := Box ;
    FXpos := X ;
    FYpos := Y ;
    FLayer := Layer ;
    FRotation := Rotation ;
    FPads := TList.Create ;
    // read shape database for <Shape> here and build pads from that
    with MedaxDataModule.Shapes do
        if FindKey( [ Shape ] ) then begin
            sType := FindField( 'Type' ).AsString ;
            FWidth := FindField( 'BoxWidth' ).AsInteger ;
            FHeight := FindField( 'BoxHeight' ).AsInteger ;
            nPinSpacing := FindField( 'PinSpacing' ).AsInteger ;
            nRowSpacing := FindField( 'RowSpacing' ).AsInteger div 2 ;
            nColumnSpacing := FindField( 'ColumnSpacing' ).AsInteger div 2 ;
            sPad := FindField( 'Pad' ).AsString ;
            FPins := FindField( 'Pins' ).AsInteger ;
            MyStack0 := TmEDAStack.Create( sPad, Rotation ) ;
            FPads.Capacity := FPins ;
            { create the outline in the layout layer }
            if Rotation in drVertical then begin
                FBox.Module.Project.AddLayerObject( Self, lcLayout, otRectangle, FXPos, FYPos, FHeight, FWidth ) ;
                i := X ;
                X := Y ;
                Y := i ;
            end
            else
                FBox.Module.Project.AddLayerObject( Self, lcLayout, otRectangle, FXPos, FYPos, FWidth, FHeight ) ;

            if LowerCase( sType ) = 'dil' then begin
                for i := 0 to FPins div 2 - 1 do begin
                    if ( FBox <> nil ) and ( FBox.Pins <> nil ) then
                        Pin := FBox.Pins.Items[ i ]
                    else
                        Pin := nil ;
                    XPad := X + ( i * nPinSpacing - ( FPins div 2 - 1 ) * nPinSpacing div 2 ) div 1000 ;
                    FPads.Add( TmEDAPad.Create( FBox.Module.Project, Self, Pin, XPad, Y - nRowSpacing, Rotation, MyStack0 ) ) ;
                    FPads.Add( TmEDAPad.Create( FBox.Module.Project, Self, Pin, XPad, Y + nRowSpacing, Rotation, MyStack0 ) ) ;
                end ;
            end
            else if ( LowerCase( sType ) = 'plcc' ) or ( LowerCase( sType ) = 'qfp' ) then begin
                MyStack1 := TmEDAStack.Create( sPad, Succ( Rotation ) ) ;
                for i := 0 to FPins div 4 - 1 do begin
                    Xpad := X + ( i * nPinSpacing - ( FPins div 4 - 1 ) * nPinSpacing div 2 ) div 1000 ;
                    if ( FBox <> nil ) and ( FBox.Pins <> nil ) then
                        Pin := FBox.Pins.Items[ i ]
                    else
                        Pin := nil ;
                    FPads.Add( TmEDAPad.Create( FBox.Module.Project, Self, Pin, XPad, Y - nColumnSpacing, Rotation, MyStack0 ) ) ;
                    FPads.Add( TmEDAPad.Create( FBox.Module.Project, Self, Pin, XPad, Y + nColumnSpacing, Rotation, MyStack0 ) ) ;
                end ;
                for i := 0 to FPins div 4 - 1 do begin
                    if ( FBox <> nil ) and ( FBox.Pins <> nil ) then
                        Pin := FBox.Pins.Items[ i ]
                    else
                        Pin := nil ;
                    YPad := Y + ( i * nPinSpacing - ( FPins div 4 - 1 ) * nPinSpacing div 2 ) div 1000 ;
                    FPads.Add( TmEDAPad.Create( FBox.Module.Project, Self, Pin, X - nRowSpacing, YPad, Rotation, MyStack1 ) ) ;
                    FPads.Add( TmEDAPad.Create( FBox.Module.Project, Self, Pin, X + nRowSpacing, YPad, Rotation, MyStack1 ) ) ;
                end ;
            end ;
        end ;
end ;

destructor TmEDAShape.Destroy ;
begin
    // free all our Pads
    with FPads do begin
        while Last <> nil do
            TmEDAPad( Last ).Free ;
        Free ;
    end ;
    inherited Destroy ;
end ;

{ TmEDABox }

constructor TmEDABox.Create( Module : TmEDAModule ; Name, Family, Part : string ; X, Y : integer ) ;
var
    sBox            : string ;
begin
    inherited Create( Name, etBox ) ;
    FPins := TList.Create ;
    FModule := Module ;
    FFamily := Family ;
    FPart := Part ;
    FXPos := X ;
    FYPos := Y ;
    with MedaxDataModule.Parts do begin
        CancelRange ;
        if FindKey( [ Family, Part ] ) then begin
            sBox := FindField( 'Box' ).AsString ;
            with MedaxDataModule.Boxes do
                if FindKey( [ sBox ] ) then begin
                    FWidth := FindField( 'Width' ).AsInteger ;
                    FHeight := FindField( 'Height' ).AsInteger ;
                    Module.AddPageObject( Self, otBox, X, Y, FWidth, FHeight ) ;
                    with MedaxDataModule.Pins do begin
                        CancelRange ;
                        SetRange( [ sBox ], [ sBox ] ) ;
                        First ;
                        while not EOF do begin
                            FPins.Add(
                                TmEDAPin.Create(
                                Self,
                                FindField( 'Label' ).AsString,
                                FindField( 'Pin' ).AsInteger,
                                FindField( 'Position' ).AsString )
                                ) ;
                            Next ;
                        end ;
                    end ;
                end ;
        end ;
    end ;
end ;

procedure TmEDABox.PlaceShape( PCBShape : string ; X, Y : integer ; Layer : TmEDALayerType ; Rotation : TmEDADir ) ;
begin
    FShape := TmEDAShape.Create( Self, PCBShape, X, Y, Layer, Rotation ) ;
end ;

{ TmEDAPin }

constructor TmEDAPin.Create( Box : TmEDABox ; Name : string ; Number : integer ; Position : string ) ;
var
    nPos            : integer ;
begin
    inherited Create( Name, etPin ) ;
    FBox := Box ;
    FNumber := Number ;
    FPos := Position ;
    nPos := StrToInt( System.Copy( Position, 2, 3 ) ) ;
    case FPos[ 1 ] of
        'T' : Box.Module.AddPageObject( Self, otTPin, Box.X + nPos + 1, Box.Y, 0, 0 ) ;
        'R' : Box.Module.AddPageObject( Self, otRPin, Box.X + Box.W + 1, Box.Y + nPos, 0, 0 ) ;
        'B' : Box.Module.AddPageObject( Self, otBPin, Box.X + nPos, Box.Y + Box.H + 1, 0, 0 ) ;
        'L' : Box.Module.AddPageObject( Self, otLPin, Box.X, Box.Y + nPos, 0, 0 ) ;
    end ;
end ;

function TmEDAPin.GetDirection : TmEDADirSet ;
begin
    case FPos[ 1 ] of
        'T' : Result := [ drNorth ] ;
        'R' : Result := [ drEast ] ;
        'B' : Result := [ drSouth ] ;
        'L' : Result := [ drWest ] ;
    end ;
end ;

function TmEDAPin.GetX : integer ;
var
    nPos            : integer ;
begin
    Result := 0 ;
    nPos := StrToInt( System.Copy( FPos, 2, 3 ) ) ;
    case FPos[ 1 ] of
        'T' : Result := Box.X + nPos + 1 ;
        'R' : Result := Box.X + Box.W + 1 ;
        'B' : Result := Box.X + nPos ;
        'L' : Result := Box.X ;
    end ;
end ;

function TmEDAPin.GetY : integer ;
var
    nPos            : integer ;
begin
    Result := 0 ;
    nPos := StrToInt( System.Copy( FPos, 2, 3 ) ) ;
    case FPos[ 1 ] of
        'T' : Result := Box.Y ;
        'R' : Result := Box.Y + nPos ;
        'B' : Result := Box.Y + Box.H + 1 ;
        'L' : Result := Box.Y + nPos ;
    end ;
end ;

{ TmEDAStack }

constructor TmEDAStack.Create( Pad : string ; Rotation : TmEDADir ) ;
var
    i               : TmEDALayerType ;
begin
    if Pad = 'Via' then begin
        FPadType := 'V' ;
        for i := lcComponent to lcInner3 do begin
            FPadWidth[ i ] := 25 ;
            FPadHeight[ i ] := 25 ;
        end ;
        FPadWidth[ lcGround ] := -25 ;
        FPadHeight[ lcPower ] := -25 ;
    end
    else
        with MedaxDataModule.Pads do
            if FindKey( [ Pad ] ) then begin
                FPadType := FindField( 'PadShape' ).AsString[ 1 ] ;
                case FPadType of
                    'C' : begin
                            FPadWidth[ lcComponent ] := FindField( 'TopPadWidth' ).AsInteger ;
                            FPadHeight[ lcComponent ] := FindField( 'TopPadWidth' ).AsInteger ;
                            FPadWidth[ lcSolder ] := FindField( 'BottomSize' ).AsInteger ;
                            FPadHeight[ lcSolder ] := FPadWidth[ lcSolder ] ;
                            for i := lcInner0 to lcInner3 do begin
                                FPadWidth[ i ] := FindField( 'InternalSize' ).AsInteger ;
                                FPadHeight[ i ] := FPadWidth[ i ] ;
                            end ;
                            FPadWidth[ lcGround ] := -FPadWidth[ lcInner0 ] ;
                            FPadHeight[ lcPower ] := -FPadWidth[ lcInner0 ] ;
                        end ;
                    'R', 'E', 'O' : begin
                            if Rotation in [ drNorth, drSouth ] then begin
                                FPadWidth[ lcComponent ] := FindField( 'TopPadWidth' ).AsInteger ;
                                FPadHeight[ lcComponent ] := FindField( 'TopPadHeight' ).AsInteger ;
                            end
                            else begin
                                FPadHeight[ lcComponent ] := FindField( 'TopPadWidth' ).AsInteger ;
                                FPadWidth[ lcComponent ] := FindField( 'TopPadHeight' ).AsInteger ;
                            end ;
                        end ;
                end ;
            end ;
end ;

function TmEDAStack.GetPadWidth( i : TmEDALayerType ) : integer ;
begin
    Result := FPadWidth[ i ]
end ;

procedure TmEDAStack.SetPadWidth( i : TmEDALayerType ; NewValue : integer ) ;
begin
    FPadWidth[ i ] := NewValue ;
end ;

function TmEDAStack.GetPadHeight( i : TmEDALayerType ) : integer ;
begin
    Result := FPadHeight[ i ]
end ;

procedure TmEDAStack.SetPadHeight( i : TmEDALayerType ; NewValue : integer ) ;
begin
    FPadHeight[ i ] := NewValue ;
end ;

{ TmEDAPad }

constructor TmEDAPad.Create( Project : TmEDAProject ; Owner : TmEDAShape ; HyperLink : TmEDAPin ; X, Y : integer ; Rotation : TmEDADir ; Stack : TmEDAStack ) ;
var
    i               : TmEDALayerType ;
begin
    if HyperLink <> nil then
        inherited Create( HyperLink.Name, etPad )
    else
        inherited Create( '', etPad ) ;
    FPin := HyperLink ;
    FShape := Owner ;
    if Rotation in [ drNorth, drSouth ] then begin
        FXpos := X ;
        FYpos := Y ;
    end
    else if Rotation in [ drNorth, drSouth ] then begin
        FXpos := Y ;
        FYpos := X ;
    end
    else begin
        FXPos := 0 ;
        FYPos := 0 ;
    end ;
    FStack := Stack ;
    case FStack.PadType of
        'V', 'E', 'C' :
            for i := lcComponent to lcLast do
                Project.AddLayerObject( Self, i, otEllipse, FXPos, FYPos, FStack.PadWidth[ i ], FStack.PadHeight[ i ] ) ;
        'R' :
            Project.AddLayerObject( Self, lcComponent, otRectangle, FXPos, FYPos, FStack.PadWidth[ lcComponent ], FStack.PadHeight[ lcComponent ] ) ;
        'O' :
            Project.AddLayerObject( Self, lcComponent, otRoundRect, FXPos, FYPos, FStack.PadWidth[ lcComponent ], FStack.PadHeight[ lcComponent ] ) ;
    end ;
end ;

destructor TmEDAPad.Destroy ;
begin
    FStack.Free ;
    inherited Destroy ;
end ;

{ TmEDAVia }

constructor TmEDAVia.Create( X, Y : integer ) ;
begin
    inherited Create ;
    FXpos := X ;
    FYpos := Y ;
    FStack := TmEDAStack.Create( 'Via', drNorth ) ;
end ;

destructor TmEDAVia.Destroy ;
begin
    FStack.Free ;
    inherited Destroy ;
end ;

{ TmEDALayer }

procedure TmEDALayer.Add( obj : TmEDALayerObject ) ;
begin
    fObjects.Add( obj ) ;
end ;

constructor TmEDALayer.Create( layer : TmEDALayerType ) ;
begin
    fObjects := TList.Create ;
end ;

destructor TmEDALayer.Destroy ;
begin
    fObjects.Free ;
    inherited ;
end ;

function TmEDALayer.GetCount : integer ;
begin
    Result := fObjects.Count ;
end ;

function TmEDALayer.GetItems( i : integer ) : TmEDALayerObject ;
begin
    Result := TObject( fObjects[ i ] ) as TmEDALayerObject ;
end ;

end.

